// CoreTests.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <LitTests.h>
#include <LitVersion.hpp>
#include <iostream>
#include <exception>


LIT_API std::ostream&  operator<<( std::ostream& strm, const Lit::Tests::StepResult &str ){ strm << str.toString(); return strm; }
LIT_API std::ostream&  operator<<( std::ostream& strm, const Lit::Tests::Step &str ){ strm << "TestStep<" << str.getName() << ">"; return strm; }
LIT_API std::ostream&  operator<<( std::ostream& strm, const Lit::Tests::AppError &str ){ strm << str.what(); return strm; }
LIT_API std::ostream&  operator<<( std::ostream& strm, Lit::Tests::AppReturnCode str )
{ 			
	switch( str )
	{
		case Lit::Tests::CODE_SUCCESS:
			strm << "Success";
			break;
		case Lit::Tests::CODE_INVALID_POINTER:
			strm << "Invalid Pointer";
			break;
		case Lit::Tests::CODE_INVALID_STREAM:
			strm << "Invalid Stream";
			break;
		case Lit::Tests::CODE_LOGIC_ERROR:
			strm << "Logic Error";
			break;
		case Lit::Tests::CODE_RUNTIME_ERROR:
			strm << "Runtime Error";
			break;
		case Lit::Tests::CODE_FATAL_ERROR:
			strm << "Fatal Error";
			break;
		default:
			strm << "Unknown";
			break;
	};
	return strm; 
}

namespace Lit
{
	namespace Tests
	{
		Step::Step( const String &name )
			:mName(name)
		{
#ifdef LIT_DEBUG
			std::cout << "Step " << name << " created !" << std::endl; 
#endif
		}
		Step::Step( const Step& rk ):mName(rk.mName){}
		Step::~Step( ){
#ifdef LIT_DEBUG
			std::cout << "Step " << mName << " destroyed !" << std::endl; 
#endif
		}
		
		Step& Step::operator=( const Step& rk ){ mName=rk.mName; return *this; }
		
		Timer::Traits Step::startTimer(){ return mTimer.start(); }
		Timer::Traits Step::stopTimer(){ return mTimer.stop(); }
		const String &Step::getName() const{ return mName; }
		const Timer&	Step::getTimer() const{ return mTimer; }

		
		AppError::AppError( const String &message, AppReturnCode code )
			: Code( code ), Message( message ), logic_error( String("Exception 0x"+String(std::to_string(code).c_str())+": "+message).data() )
		{}
		AppError::AppError( const AppError & err ) 
			: Code( err.Code ), Message( err.Message ), logic_error(err)
		{}
		int AppError::getCode() const{ return Code; }
		const String & AppError::getMessage() const{ return Message; }

		AppError& AppError::operator=( const AppError & err ){ Code = err.Code; Message = err.Message; logic_error::operator=( err ); return *this; }
		
		StepResult::StepResult(){}
		StepResult::StepResult( const String &error, const String &name, int code, const Timer &timer ) : mError( error ), mName(name), mCode(code), mTimer( timer ){}
		StepResult::StepResult(const StepResult& rk):mName(rk.mName),mError(rk.mError),mCode(rk.mCode),mTimer(rk.mTimer){}
		StepResult::~StepResult(){}

		StepResult& StepResult::operator = (const StepResult& rk){ mName=rk.mName; mError=rk.mError; mCode=rk.mCode; mTimer = rk.mTimer; return *this; }
		
		String StepResult::toString() const{
			if( mCode == CODE_SUCCESS )
				return "Step '" + getName() + "' ran successfully in " + std::to_string(getTimer().elapsedMSec()).c_str() + " msec";
			else
				return "Step '" + getName() + "' produced errors, details: \r\n"
						+	"\t" + "Time: " + std::to_string(mTimer.elapsedMSec()).c_str() + " msec\r\n"
						+	String("\t") + "ErrorCode: 0x" + std::to_string(getCode()).c_str() + "\r\n"
						+	"\t" + "Error: " + mError;
		}
		const String&		StepResult::getError() const{ return mError; }
		const String&		StepResult::getName() const{ return mName; }
		int						StepResult::getCode() const{ return mCode; }
		const Timer&			StepResult::getTimer() const{ return mTimer; }


		App::App( const String &app_name )
			: mName( app_name )
			, mSteps( new StepStack )
		{
#			pragma comment( user, "Creating Test Suite for platform '" LIT_PLATFORM_NAME " (" LIT_ARCHITECTURE_NAME ")'" ) 
			system("cls");
			std::cout << "Starting Test Suite for CoreLibrary"	<< std::endl
						<< "---------------------------------"  << std::endl
						<< "Version: " << Lit::Version<>().toString().data() << std::endl
						<< "Platform: " << LIT_PLATFORM_NAME << std::endl
						<< "Architecture: " << LIT_ARCHITECTURE_NAME << std::endl
						<< "at " << __TIME__ << std::endl 
						<< std::endl;
		}
		App::~App()
		{
		}
		void App::processStepError( const StepResult& result ){}
		void App::addStep( Step * step ){ mSteps->push( StepPtr(step) ); }
		int App::_processReturns( const StepResultMap &rets )
		{
			bool err=false;
			for( StepResultMap::const_iterator it = rets.begin();
				it !=rets.end();
				it ++ )
			{
				processStepError( it->second );
				if( it->second.getCode() != CODE_SUCCESS )
				{
					err = true;
				}
				std::cout << it->second << std::endl;
			}
			if( err )
			{
				return rets.empty() ? CODE_RUNTIME_ERROR : rets.begin()->second.getCode();
			}
			return CODE_SUCCESS;
		}
		int	App::start( )
		{
			StepResultMap ret;
			while( !mSteps->empty() )
			{
				StepPtr & p(mSteps->top());
				ret[p->getName()] = _startStep( mSteps->top() );
				mSteps->pop();
			}

			return _processReturns( ret );
		}

		StepResult App::_startStep( StepPtr & ptr )
		{
			int ret = -1;
			std::cout	<< "Starting Step " << ptr->getName() 
						<< std::endl;
			String err;
			try
			{
				ptr->startTimer();
				ret = ptr->work();
			}
			catch( AppError e )
			{
				std::cerr << "\t" << e.what() << std::endl;
				ret = e.getCode(); err = e.getMessage();
			}
			catch( std::exception e )
			{
				std::cerr << "\tException: " << e.what() << std::endl;
				ret = CODE_RUNTIME_ERROR;
				err = e.what();
			}
			catch( ... )
			{
				std::cerr << "\tUnknown exception" << std::endl;
				err = "Unknown error";
				ret = CODE_FATAL_ERROR;
			}
			ptr->stopTimer();
			std::cout << "Stopping Step " << ptr->getName() << std::endl << std::endl;
			return StepResult( err, ptr->getName(), ret, ptr->getTimer() );
		}

	};
};